package com.ideal.gpt.app.docqajob;

import java.io.File;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.ideal.gpt.GptJobConfig;
import com.ideal.gpt.ability.common.NlpJob;
import com.ideal.gpt.ability.docqa.controller.DocQaController;
import com.ideal.gpt.app.docqajob.QaParseJob.JobInfo;
import com.ideal.gpt.app.docqajob.localjob.ApiTest;
import com.lzwork.utility.TimeTicker;
import com.lzwork.utility.TimeTicker.TickFormat;
import com.lzwork.utility.collection.CollUtility;
import com.lzwork.utility.collection.MapSorter.SortType;
import com.lzwork.utility.counter.ItemCounter;
import com.lzwork.utility.data.NumberParse;
import com.lzwork.utility.file.FileUtility;
import com.lzwork.utility.file.LineContent;
import com.lzwork.utility.logger.LOGGER;
import com.lzwork.utility.str.StrUtility;
import com.lzwork.web.HttpRequester;
import com.lzwork.web.HttpRequester.RequestData;
import com.lzwork.web.HttpRequester.RequestType;
import com.lzwork.web.HttpRequester.ReturnData;
import com.lzwork.web.JsonKey;

/**
 * @author LiZheng
 * @date 2023年8月29日 下午5:03:15
 */

@Controller
@RequestMapping(value = "/qajob/*")
public class JobController extends DocQaController
{
	int fileNum = 3;

	JobInfo createInfo()
	{
		init();

		JobInfo info = new JobInfo();
		info.url = GptJobConfig.url();
		info.logFile = new File("./logs.txt");

		return info;
	}

	File folder()
	{
		return folder(null);
	}

	File folder(String folder)
	{
		if (!StrUtility.stringValid(folder))
			folder = GptJobConfig.getProp("folder");

		return FileUtility.getFileSafe(GptJobConfig.root(), folder);
	}

	File[] filePool = null;
	Map<String, String> fileIndex = new HashMap<>();

	void init()
	{
		init(false);
	}

	void init(boolean reload)
	{
		if (filePool != null && !reload)
			return;

		List<File> files = new ArrayList<>();
		File source = folder();
		fileIndex.clear();

		if (!source.exists())
		{
			LOGGER.warn(LOGGER.FILE, "Source folder: " + source.getAbsolutePath() + " is not exit.");
			return;
		}
		if (!source.isDirectory())
		{
			LOGGER.warn(LOGGER.FILE, "Source folder: " + source.getAbsolutePath() + " is not a folder.");
			return;
		}

		LOGGER.log(LOGGER.FILE, "Load qa sample files from source folder: " + source.getAbsolutePath());

		for (File f : source.listFiles())
		{
			if (f.isFile() && f.length() < 5000)
			{
				files.add(f);
			}
		}

		filePool = files.toArray(new File[files.size()]);
		LOGGER.log(LOGGER.FILE, "Total " + filePool.length + " source files loaded.");

		String indexFileName = GptJobConfig.getProp("index");

		File indexFile = FileUtility.getFileSafe(GptJobConfig.root(), indexFileName);

		if (indexFile.exists())
		{
			List<String> lines = LineContent.loadFile(indexFile);

			for (String line : lines)
			{
				int i = line.indexOf('\t');
				if (i > 0 && i < line.length() - 1)
				{
					String now = line.substring(0, i);
					String real = line.substring(i + 1);

					if (StrUtility.stringValid(true, now, real))
					{
						fileIndex.put(now, real);
					}
				}
			}
		}
	}

	JSONObject fileJobj(File file, int index)
	{
		String name = file.getName();
		if (fileIndex.containsKey(name))
			name = fileIndex.get(name);

		JSONObject obj = new JSONObject();
		obj.put("id", index);
		obj.put("name", name);
		obj.put("size", FileUtility.getFileSizeString(file.length()));

		String content = LineContent.loadString(file);
		obj.put("content", content);
		obj.put("len", NumberParse.numToKiloCount(content.length()));

		return obj;
	}

	@GetMapping(value = "/info")
	@ResponseBody
	public JSONObject info(@RequestParam(value = "filelist", defaultValue = "true") Boolean filelist,
			@RequestParam(value = "debug", defaultValue = "") Boolean debug)
	{
		init();
		JSONObject result = null;

		boolean isDebug = debug == null ? NumberParse.objToBool(GptJobConfig.getProp("debug"), false) : debug;

		if (isDebug)
		{
			String infoUrl = GptJobConfig.url() + "/" + GptJobConfig.getProp("modelinfo");

			RequestData requestData = new RequestData(RequestType.GET, infoUrl, "", null);
			ReturnData responseData = HttpRequester.doHttpRequest(requestData);

			Exception e = responseData.getException();
			if (e != null)
			{
				result = new JSONObject();
				result.put(JsonKey.KEY_INFO, e.getMessage());
			} else
				result = responseData.getJson();
		}

		if (result == null)
			result = new JSONObject();

		if (filelist == true && filePool != null)
		{
			List<JSONObject> files = new ArrayList<>();

			if (filePool.length <= fileNum)
			{
				for (int i = 0; i < filePool.length; i++)
				{
					File f = filePool[i];
					files.add(fileJobj(f, i));
				}
			} else
			{
				int num = Math.min(3, filePool.length);

				Set<Integer> index = CollUtility.getRandomIndex(filePool.length, num);

				for (int i : index)
				{
					File f = filePool[i];
					files.add(fileJobj(f, i));
				}
			}

			result.put("files", files);
		}
		result.put("debug", isDebug);

		return result;
	}

	@PostMapping(value = "/apitest", produces = "application/json;charset=UTF-8")
	@ResponseBody
	public JSONObject apitest(@RequestBody Map<?, ?> map)
	{
		String api = getMapStr(map, "api");
		int num = getMapInt(map, "num", 1);
		int limit = getMapInt(map, "limit", 100);
		String output = getMapStr(map, "output", "apitest.xlsx");
		boolean dolog = getMapBool(map, "dolog", false);

		init(false);

		File outputFile = FileUtility.getFileSafe(GptJobConfig.root(), output);
		ApiTest test = new ApiTest(api, dolog);
		test.doTest(filePool, fileIndex, limit, num, outputFile);

		JSONObject result = new JSONObject();
		return result;
	}

	@PostMapping(value = "/doJob", produces = "application/json;charset=UTF-8")
	@ResponseBody
	public JSONObject doJob(@RequestBody Map<?, ?> map) throws NoSuchAlgorithmException
	{
		int size = getMapInt(map, "size", 2000);
		int num = getMapInt(map, "num", 3);
		int type = getMapInt(map, "type", 1);
		boolean breatNetException = getMapBool(map, "netbreak");

		String folder = getMapStr(map, "folder");
		String url = getMapStr(map, "url");
		String title = getMapStr(map, "title");

		JobInfo info = createInfo();
		info.maxLen = size;
		info.num = num;
		info.breatNetException = breatNetException;

		if (StrUtility.stringValid(url))
			info.url = url;

		QaParseJob job = new QaParseJob(info);

		TimeTicker ticker = new TimeTicker();
		File source = folder(folder);

		LOGGER.log(LOGGER.APP, "Job from source: " + source.getAbsolutePath());
		File output = FileUtility.getFileSafe(GptJobConfig.root(),
				"/parse_output" + title + "_T" + type + "_N" + num + "_S" + size + ".xlsx");

		int parsed = job.parseFromFolder(type, source, output, true, false);

		JSONObject result = new JSONObject();

		result.put("parsed", parsed);
		result.put("cost", ticker.tickS(TickFormat.Auto, false));
		result.put("err", StrUtility.combineString(job.errLog, "\n"));

		LOGGER.log(LOGGER.APP, result.toString(SerializerFeature.PrettyFormat));

		return result;
	}

	JobInfo rInfo = null;

	JobInfo runInfo()
	{
		if (rInfo == null)
			rInfo = createInfo();
		return rInfo;
	}

	@PostMapping(value = "/chat", produces = "application/json;charset=UTF-8")
	@ResponseBody
	public JSONObject chat(@RequestBody Map<?, ?> map)
	{
		return super.chat(map);
	}

	@PostMapping(value = "/parse", produces = "application/json;charset=UTF-8")
	@ResponseBody
	public JSONObject parse(@RequestBody Map<?, ?> map)
	{
		return super.parse(map);
	}

	@PostMapping(value = "/segcount", produces = "application/json;charset=UTF-8")
	@ResponseBody
	public JSONObject segcount(@RequestBody Map<?, ?> map)
	{
		JSONObject result = new JSONObject();

		String content = getMapStr(map, "content");

		TimeTicker ticker = new TimeTicker();

		List<String> words = NlpJob.splitCoreWords(content);

		ItemCounter<String> counter = new ItemCounter<>();
		counter.addItems(words);

		List<Entry<String, Integer>> sorted = counter.getSortedEntry(SortType.DECREASING);

		List<JSONObject> contentObj = new ArrayList<>();

		for (Entry<String, Integer> e : sorted)
		{
			JSONObject o = new JSONObject();
			o.put("key", e.getKey());
			o.put("num", e.getValue());
			contentObj.add(o);
		}

		result.put("content", contentObj);
		result.put("cost", ticker.tickS());

		return result;
	}

	@GetMapping(value = "/reset", produces = "application/json;charset=UTF-8")
	@ResponseBody
	public JSONObject reset()
	{
		RequestData requestData = new RequestData(RequestType.GET, GptJobConfig.url() + "/reset", "", null);
		ReturnData responseData = HttpRequester.doHttpRequest(requestData);

		Exception e = responseData.getException();
		JSONObject result;
		if (e != null)
		{
			result = new JSONObject();
			result.put(JsonKey.KEY_INFO, e.getMessage());
		} else
			result = responseData.getJson();
		return result;
	}
}
